﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Pikapika.Models;
using Pikapika.Models.ViewModels.Novel;
using System.Diagnostics;
using System.Data.Objects;
using System.IO;

namespace Pikapika.Controllers
{
    public class NovelController : LayoutPageControllerBase
    {
        //
        // GET: /Novel/

        public ActionResult Index()
        {
            return List(null, 1);
        }

        private NovelDisplayModel CreateNovelDisplayModel(PikapikaDBEntities ctx, Novel novel)
        {
            var ret = new NovelDisplayModel();
            ret.Novel = novel;
            ret.AnnotationCount = (from annotation in ctx.Annotations
                                   where annotation.NovelId == novel.Id
                                   select annotation.Id).Count();
            ret.HitCount = (from hitCountRecord in ctx.NovelChapterHitCountRecords
                            where hitCountRecord.NovelId == novel.Id
                            select (int?)hitCountRecord.HitCount).Sum().GetValueOrDefault();

            if (novel.NovelVolumes.IsLoaded)
            {
                ret.LatestChapter = (from vol in novel.NovelVolumes
                                     from chap in vol.NovelChapters
                                     orderby chap.UpdatedDate descending, chap.SortIndex descending, chap.Id descending
                                     select chap).FirstOrDefault();
            }
            else
            {
                ret.LatestChapter = (from chap in ctx.NovelChapters
                                     where chap.NovelId == novel.Id
                                     orderby chap.UpdatedDate descending, chap.SortIndex descending, chap.Id descending
                                     select chap).FirstOrDefault();
            }

            if (ret.LatestChapter != null)
            {
                ret.LatestVolume = ret.LatestChapter.NovelVolume;
            }
            ret.UpdatedDate = new DateTimeOffset(novel.UpdatedDate, TimeSpan.Zero);
            return ret;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="id">Publisher Id</param>
        /// <param name="page">Page number(starting from 1)</param>
        /// <returns></returns>
        public ActionResult List(int? id, int page)
        {
            if (id.GetValueOrDefault() < 1)
            {
                id = null;
            }
            using (var ctx = new PikapikaDBEntities())
            {
                ctx.Connection.Open();
                using (ctx.Connection)
                {
                    var result = new NovelListModel();
                    if (id.HasValue)
                    {
                        result.Publisher = ctx.Publishers.Where(p => p.Id == id).SingleOrDefault();
                        if (result.Publisher == null)
                            return HttpNotFound();
                    }

                    int pageSize = Properties.Settings.Default.PageSize;
                    Debug.Assert(pageSize > 0);

                    IQueryable<Novel> query;

                    if (id.HasValue)
                    {
                        query = ctx.Novels.Where((n) => n.PublisherId == id.Value);
                    }
                    else
                    {
                        query = ctx.Novels;
                    }
                    result.PageCount = (int)Math.Ceiling(((double)query.Count()) / pageSize);

                    query = query
                            .OrderByDescending(n => n.UpdatedDate)
                            .Skip(pageSize * (page - 1))
                            .Take(pageSize);
                    query = ((ObjectQuery<Novel>)query);

                    // return Content(((ObjectQuery<Novel>)query).ToTraceString(), "text/plain");

                    result.Novels = query.AsEnumerable().Select((n) => CreateNovelDisplayModel(ctx, n)).ToList();
                    result.CurrentPage = page;
                    // result.CurrentPage = 35;
                    // result.PageCount = 50;
                    return View(result);
                }
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="id">Novel ID</param>
        /// <returns></returns>
        public ActionResult Details(int id)
        {
            using (var ctx = new PikapikaDBEntities())
            {
                ctx.ContextOptions.LazyLoadingEnabled = false;
                ctx.Connection.Open();
                var query = (from n in ctx.Novels
                              where n.Id == id
                              select n);
                query = ((ObjectQuery<Novel>)query).Include("Publisher").IncludeNovelChapters().Include("NovelTags");

                var result = query.SingleOrDefault();
                if (result == null)
                    return HttpNotFound();

                return View(CreateNovelDisplayModel(ctx, result));
            }
        }

        public ActionResult Read(int id)
        {
            using (var ctx = new PikapikaDBEntities())
            {
                // we may query more than once
                ctx.Connection.Open();

                try
                {
                    var query = (from n in ctx.NovelChapters
                                 where n.Id == id
                                 select n);
                    query = ((ObjectQuery<NovelChapter>)query).Include("NovelVolume.NovelChapters")
                                                              .Include("NovelChapterHitCountRecords")
                                                              .Include("NovelVolume.Novel");

                    var requestedChapter = query.SingleOrDefault();
                    if (requestedChapter == null)
                        return HttpNotFound();

                    ctx.sp_IncreaseHitCountForChapter(requestedChapter.Id);

                    var viewModel = new NovelReadModel();
                    viewModel.Chapter = requestedChapter;
                    viewModel.HitCount = requestedChapter.NovelChapterHitCountRecords.Sum(r => r.HitCount) + 1;

                    var volumeChapters = requestedChapter.NovelVolume.GetSortedChapters().ToList();

                    var chapterIndex = volumeChapters.FindIndex(c => c.Id == requestedChapter.Id);

                    if (chapterIndex < 0)
                    {
                        throw new InvalidOperationException("The chapter is orphaned.");
                    }

                    if (chapterIndex > 0)
                    {
                        viewModel.PreviousChapter = volumeChapters[chapterIndex - 1];
                    }

                    if (chapterIndex < volumeChapters.Count - 1)
                    {
                        viewModel.NextChapter = volumeChapters[chapterIndex + 1];
                    }

                    List<NovelChapter> allChapters = null;
                    int chapterIndexInAllChapters = -1;
                    Action loadAllChapters = () =>
                    {
                        allChapters = (from volume in requestedChapter.NovelVolume.Novel.NovelVolumes.CreateSourceQuery()
                                       from chapter in volume.NovelChapters
                                       orderby volume.SortIndex ascending, volume.Id ascending, chapter.SortIndex ascending, chapter.Id ascending
                                       select chapter).ToList();
                        chapterIndexInAllChapters = allChapters.FindIndex(ch => ch.Id == requestedChapter.Id);
                        if (chapterIndexInAllChapters < 0)
                        {
                            throw new InvalidOperationException("The chapter is orphaned.");
                        }
                    };
                    if (viewModel.PreviousChapter == null)
                    {
                        loadAllChapters();
                        if (chapterIndexInAllChapters > 0)
                        {
                            viewModel.PreviousChapter = allChapters[chapterIndexInAllChapters - 1];
                            ctx.LoadProperty(viewModel.PreviousChapter, (ch) => ch.NovelVolume);
                        }
                    }

                    if (viewModel.NextChapter == null)
                    {
                        if (allChapters == null)
                        {
                            loadAllChapters();
                        }
                        if (chapterIndexInAllChapters < allChapters.Count - 1)
                        {
                            viewModel.NextChapter = allChapters[chapterIndexInAllChapters + 1];
                            ctx.LoadProperty(viewModel.NextChapter, (ch) => ch.NovelVolume);
                        }
                    }

                    return View(viewModel);
                }
                finally
                {
                    ctx.Connection.Close();
                }
            }
        }

    }
}
